package trafficgeneratorcr.algo;

import java.util.*;

public class GeneratorAlgorithm {

    // Input attributes

    public static final int NUMBER_OF_MIN_PER_WEEK = 10080;

    private int numberOfWeeks;
    private int numberOfMinutesPerTimeSlice;
    private int numberOfPacketsPerWeek;
    private double[][][] distributionPointsEveryMinute;

    // Output attributes
    private int timeSliceCountPerWeek;
    private int outputTables[][][];

    // Constructor
    public GeneratorAlgorithm(int numberOfWeeks, int numberOfMinutesPerTimeSlice, int numberOfPacketsPerWeek, double[][][] distributionPointsEveryHour) {
        this.numberOfWeeks = numberOfWeeks;
        this.numberOfMinutesPerTimeSlice = numberOfMinutesPerTimeSlice;
        this.numberOfPacketsPerWeek = numberOfPacketsPerWeek;
        this.timeSliceCountPerWeek = NUMBER_OF_MIN_PER_WEEK / numberOfMinutesPerTimeSlice;

        this.distributionPointsEveryMinute = preparePoints(distributionPointsEveryHour);

        this.outputTables = new int [distributionPointsEveryHour.length][numberOfWeeks][timeSliceCountPerWeek];
    }
    
    public int [][][] getOutputTables(){
        return outputTables;
    }
    public double[][][] getDistributionPointsEveryMinute() {
        return distributionPointsEveryMinute;
    }

    private static double[][][] preparePoints(double[][][] distributionPointsEveryHour) {
        double[][][] normalisedDistributionPointsEveryMinute = 
                new double[distributionPointsEveryHour.length][distributionPointsEveryHour[0].length][60 * 24 + 1];

        for (int type = 0; type < distributionPointsEveryHour.length; type++) {
            for (int dayOfWeek = 0; dayOfWeek < distributionPointsEveryHour[type].length; dayOfWeek++){
                double[] tempDistributionPointsEveryHour = new double[distributionPointsEveryHour[type][dayOfWeek].length];
                for (int i = 0; i < tempDistributionPointsEveryHour.length; i++) {
                    tempDistributionPointsEveryHour[i] = distributionPointsEveryHour[type][dayOfWeek][i];
                }

                InterpolationAlgorithm interpolationAlgorithm = new LinearInterpolationAlgorithm(tempDistributionPointsEveryHour, 60 * 24 + 1);
                interpolationAlgorithm.execute();
                double[] interpolatedPoints = interpolationAlgorithm.getInterpolatedPoints();

                NormalisationAlgorithm normalisationAlgorithm = new NormalisationAlgorithm(interpolatedPoints);
                normalisationAlgorithm.execute();
                double[] normalisedPoints = normalisationAlgorithm.getOutputPoints();

                for (int minuteOfDay = 0; minuteOfDay < 60 * 24 + 1; minuteOfDay++) {
                    normalisedDistributionPointsEveryMinute[type][dayOfWeek][minuteOfDay] = normalisedPoints[minuteOfDay];
                }
            }
        }
        
        return normalisedDistributionPointsEveryMinute;
    }


   // Algorithm
    public void execute() {
        int numberOfPacketsPerDay = numberOfPacketsPerWeek / 7;
        int timeSliceCountPerDay = timeSliceCountPerWeek / 7;

        for (int type = 0; type < distributionPointsEveryMinute.length; type++) {
            for (int weekIndex = 0; weekIndex < numberOfWeeks; weekIndex++) {
                for (int dayOfWeek = 0; dayOfWeek < distributionPointsEveryMinute[type].length; dayOfWeek++) {
                    // allocate slots for numbers of packets generated every minute
                    int[] packetCountOfEveryMinutePerDay = new int[60 * 24 + 1];

                    // compute the number of packets generated every minute
                    // actually "generate" the packets
                    for (int packetIndex = 0; packetIndex < numberOfPacketsPerDay; packetIndex++) {
                        int minuteOfDay = myRandom(distributionPointsEveryMinute[type][dayOfWeek]);
                        packetCountOfEveryMinutePerDay[minuteOfDay]++;
                    }

                    // sum all the packets in every time slice and
                    // put them into outputTables
                    for (int timeSliceIndexOfDay = 0; timeSliceIndexOfDay < timeSliceCountPerDay; timeSliceIndexOfDay++) {
                        int sum = 0;
                        for (int minuteOfSlice = 0; minuteOfSlice < numberOfMinutesPerTimeSlice; minuteOfSlice++) {
                            int minuteOfDay = timeSliceIndexOfDay * numberOfMinutesPerTimeSlice + minuteOfSlice;
                            sum += packetCountOfEveryMinutePerDay[minuteOfDay];
                        }

                        int timeSliceIndexOfWeek = timeSliceIndexOfDay + dayOfWeek * timeSliceCountPerDay;
                        outputTables[type][weekIndex][timeSliceIndexOfWeek] = sum;
                    }
                }
            }
        }
    }

    private static int myRandom(double[] distributionPointsEveryMinute) {
        double uniformValue = new Random().nextDouble();
        int index = 0;
        double sum = 0;

        while (sum < uniformValue) {
             sum = sum + distributionPointsEveryMinute[index];
             index++;
        }

        return index - 1;
    }


}

